import redis

from zz import zz_code, zz_return, zz_log
import traceback

log = zz_log.of()

client = None


def of():
    global client
    if client is None:
        client = ZZ_Redis()

    return client


class ZZ_Redis:
    def __init__(self):
        host = zz_code.env("REDIS_HOST")
        port = zz_code.env("REDIS_PORT", "6378")
        pwd = zz_code.env("REDIS_PWD", "6378")

        self.client = redis.Redis(host=host, port=port, password=pwd)

    def get_client(self) -> redis.Redis:
        return self.client

    def set_if_exists(self, key, val, timeout_s) -> bool:
        script = """
        local key = KEYS[1]
        local value = ARGV[1]
        local seconds = ARGV[2]        
        local set_ok = redis.call('setnx', key, value)
        if set_ok == 1 then
            redis.call('expire', key, seconds)
        end
        return set_ok    
        """
        cmd = self.client.register_script(script)
        set_ok = cmd(keys=[key], args=[val, timeout_s])
        return set_ok == 1  # 执行脚本

    def delete_by_prefix(self, prefix):
        """
        删除指定前缀的key，例：abc，删除abc开关的key
        :param prefix:
        """
        keys = self.client.keys(prefix + "*")
        for key in keys:
            self.client.delete(key)

    def exists(self, key) -> bool:
        """
        key是否存在
        :param key:
        """
        return self.client.exists(key) == 1

    def del_if_equals_val(self, key, val) -> bool:
        """
        val值等于才执行删除
        :param key:
        """
        script = """
                local key = KEYS[1]
                local value = ARGV[1]
                local curr_value = redis.call('get', key);
                if curr_value == value then
                    redis.call('del', key);
                    return 1;
                end
                return 0;  
                """
        cmd = self.client.register_script(script)
        ok = cmd(keys=[key], args=[val])
        log.debug(f"del_if_equals_val: {ok}")
        return ok == 1  # 执行脚本

    def try_lock_fun(self, key, func, timeout_seconds=60) -> zz_return.Return:
        """
        获取不到锁，立即返回不等待
        """
        if timeout_seconds < 0:
            return zz_return.of(20063, "timeoutSeconds error")

        token = zz_code.get_uuid()
        set_ok = self.set_if_exists(key, token, timeout_seconds)
        if not set_ok:
            return zz_return.of(10100, "获取锁失败")

        r = zz_return.of(10110, "REDIS事件异常")
        try:
            r = func()
        except Exception as ex:
            traceback.print_exc()
            r.set_errmsg(f"{ex}")
        finally:
            self.del_if_equals_val(key, token)

        return r
